/** 注释
 * @name tooltip
 * @Author FiveXu(316643470@qq.com)
 * @Description: tooltip
 * @Date  2021-06-29 23:15
 */
import { App, createApp, DirectiveBinding } from 'vue'
import TooltipVue, { TooltipPositionArr, TooltipProps, TooltipPosition } from './tooltips.vue'
import { ComponentPublicInstance } from '@vue/runtime-core'
import { getZIndex } from '@/base/hooks'

// 提示条显示距离位置
const tooltipMargin = 10

interface TooltipInstance extends ComponentPublicInstance {
  setTitleValue?: (val: string | number) => void
}

interface TooltipEl extends HTMLElement {
  tooltipInstance: TooltipInstance | undefined;
  tooltipTimer: ReturnType<typeof setTimeout> | null;
}

// 提示语位置
const tooltipPosition = (position: TooltipPosition, tooltipDom: HTMLElement, mountDomRect: DOMRect): void => {
  tooltipDom.style.zIndex = String(getZIndex())
  switch (position) {
    case 'topLeft':
      tooltipDom.style.left = `${mountDomRect.left}px`
      tooltipDom.style.top = `${mountDomRect.top - tooltipMargin}px`
      break
    case 'top':
      tooltipDom.style.left = `${mountDomRect.left + mountDomRect.width / 2}px`
      tooltipDom.style.top = `${mountDomRect.top - tooltipMargin}px`
      break
    case 'topRight':
      tooltipDom.style.left = `${mountDomRect.left + mountDomRect.width}px`
      tooltipDom.style.top = `${mountDomRect.top - tooltipMargin}px`
      break
    case 'rightTop':
      tooltipDom.style.left = `${mountDomRect.left + mountDomRect.width + tooltipMargin}px`
      tooltipDom.style.top = `${mountDomRect.top}px`
      break
    case 'right':
      tooltipDom.style.left = `${mountDomRect.left + mountDomRect.width + tooltipMargin}px`
      tooltipDom.style.top = `${mountDomRect.top + mountDomRect.height / 2}px`
      break
    case 'rightBottom':
      tooltipDom.style.left = `${mountDomRect.left + mountDomRect.width + tooltipMargin}px`
      tooltipDom.style.top = `${mountDomRect.top + mountDomRect.height}px`
      break
    case 'leftTop':
      tooltipDom.style.left = `${mountDomRect.left - tooltipMargin}px`
      tooltipDom.style.top = `${mountDomRect.top}px`
      break
    case 'left':
      tooltipDom.style.left = `${mountDomRect.left - tooltipMargin}px`
      tooltipDom.style.top = `${mountDomRect.top + mountDomRect.height / 2}px`
      break
    case 'leftBottom':
      tooltipDom.style.left = `${mountDomRect.left - tooltipMargin}px`
      tooltipDom.style.top = `${mountDomRect.top + mountDomRect.height}px`
      break
    case 'bottomLeft':
      tooltipDom.style.left = `${mountDomRect.left}px`
      tooltipDom.style.top = `${mountDomRect.top + mountDomRect.height + tooltipMargin}px`
      break
    case 'bottom':
      tooltipDom.style.left = `${mountDomRect.left + mountDomRect.width / 2}px`
      tooltipDom.style.top = `${mountDomRect.top + mountDomRect.height + tooltipMargin}px`
      break
    case 'bottomRight':
      tooltipDom.style.left = `${mountDomRect.left + mountDomRect.width}px`
      tooltipDom.style.top = `${mountDomRect.top + mountDomRect.height + tooltipMargin}px`
      break
    default:
      tooltipDom.style.left = `${mountDomRect.left + mountDomRect.width / 2}px`
      tooltipDom.style.top = `${mountDomRect.top - tooltipMargin}px`
      break
  }
}
// 初始化提示
const initTooltip = (el: TooltipEl, binding: DirectiveBinding, isChange = true): HTMLElement => {
  const maxWidth = el.getAttribute('fx-tooltip-width')
  const color = el.getAttribute('fx-tooltip-color')
  const plain = el.getAttribute('fx-tooltip-plain')
  const mountDomRect = el.getBoundingClientRect()
  const modifiers = Object.keys(binding.modifiers)
  const position = modifiers.find(item => binding.modifiers[item])
  const isPosition = position && TooltipPositionArr.includes(position)
  if (!el.tooltipInstance) {
    const dom: HTMLElement = document.createElement('div')
    const prop: Partial<TooltipProps> = {
      title: binding.value,
      position: isPosition ? position as TooltipPosition : 'top',
      width: maxWidth || 220,
      color: color || '',
      plain: plain !== null
    }
    el.tooltipInstance = createApp(TooltipVue, prop).mount(dom)
  } else {
    if (isChange && el.tooltipInstance?.setTitleValue) el.tooltipInstance.setTitleValue(binding.value)
    el.tooltipInstance.$el.style.display = 'block'
  }
  const tooltipDom: HTMLElement = el.tooltipInstance.$el
  tooltipPosition(position as TooltipPosition, tooltipDom, mountDomRect)
  return tooltipDom
}
// 移除提示
const removeTooltip = (el: TooltipEl): void => {
  if (!el.tooltipInstance) return
  el.tooltipInstance.$el.remove()
}
// 鼠标移入
const mouseenter = (el: TooltipEl, binding: DirectiveBinding): () => void => {
  return () => {
    const time = el.getAttribute('fx-tooltip-time') || 0
    el.tooltipTimer && clearTimeout(el.tooltipTimer)
    el.tooltipTimer = setTimeout(() => {
      const textOver = el.getAttribute('fx-tooltip-text-over')
      if (textOver !== null && el.scrollWidth <= el.clientWidth) return
      document.body.appendChild(initTooltip(el, binding, false))
    }, Number(time))
  }
}
// 鼠标移出
const mouseleave = (el: TooltipEl): () => void => {
  return () => {
    el.tooltipTimer && clearTimeout(el.tooltipTimer)
    if (el.tooltipInstance?.$el) el.tooltipInstance.$el.style.display = 'none'
  }
}

const Tooltip = (app: App): void => {
  app.directive('tooltip', {
    mounted (el: TooltipEl, binding) {
      const textOver = el.getAttribute('fx-tooltip-text-over')
      if (textOver !== null) {
        el.style.whiteSpace = 'nowrap'
        el.style.textOverflow = 'ellipsis'
        el.style.overflow = 'hidden'
      }
      el.addEventListener('mouseenter', mouseenter(el, binding))
      el.addEventListener('mouseleave', mouseleave(el))
    },
    updated (el: TooltipEl, binding: DirectiveBinding) {
      if (el.tooltipInstance?.setTitleValue) el.tooltipInstance.setTitleValue(binding.value)
    },
    beforeUnmount (el: TooltipEl, binding) {
      removeTooltip(el)
      el.removeEventListener('mouseenter', mouseenter(el, binding))
      el.removeEventListener('mouseleave', mouseleave(el))
    }
  })
}

export default Tooltip
